﻿using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace Mars.GpuInstance
{
    [CustomEditor(typeof(GPUAnimatorInstancerForBake))]
    public class GPUAnimatorInstancerForBakeEditor : Editor
    {
        private GPUAnimatorInstancerForBake m_Target;
        private GPUAnimationBaker m_Baker;


        private void OnEnable()
        {
            m_Target = (GPUAnimatorInstancerForBake) target;
            if (m_Baker == null)
            {
                m_Baker = new GPUAnimationBaker();
            }
            
            m_Target.LodDatas.Sort();
        }

        public override void OnInspectorGUI()
        {
            serializedObject.Update();
            DrawPropertiesExcluding(serializedObject);

            if (GUILayout.Button("刷新"))
            {
                m_Target.RefreshName();
                AutoAddLodGroup(m_Target);
                EditorUtility.SetDirty(m_Target);
                AssetDatabase.SaveAssets();
                AssetDatabase.Refresh();
            }

            if (GUILayout.Button("烘焙动画"))
            {
                SkinnedMeshBakeAnimations();
            }
            
            
            serializedObject.ApplyModifiedProperties();
            
            bool hasEmptyRenderData = false;
            string lostStr = "";
            for (int i = 0; i < m_Target.LodDatas.Count; i++)
            {
                var lodData = m_Target.LodDatas[i];
                if (lodData.renderDatas.Count <= 0)
                {
                    hasEmptyRenderData = true;
                    lostStr += $"lod:{i} 渲染数据为空\n";
                }
            }

            for (int i = 0; i < m_Target.LodDatas.Count; i++)
            {
                var lodData = m_Target.LodDatas[i];
                for (int j = 0; j < lodData.renderDatas.Count; j++)
                {
                    var renderData = lodData.renderDatas[j];
                    if (renderData.meshRenderer == null)
                    {
                        hasEmptyRenderData = true;
                        lostStr += $"lod:{i}的第{j}个网格丢失\n";
                    }
                }
            }
            

            if (hasEmptyRenderData)
            {
                EditorGUILayout.HelpBox(lostStr, MessageType.Error);
            }
        }
        
        
        public void EditorCallSkinnedMeshBakeAnimations()
        {
            SkinnedMeshBakeAnimations();
        }
        
        //为了给其他地方用工具调用多弄出来的一个烘培动画处理接口
        private void SkinnedMeshBakeAnimations()
        {
            m_Target.RefreshName();
            m_Baker.SkinnedMeshBakeAnimations(m_Target, () =>
            {
                EditorUtility.SetDirty(m_Target);
                CreateGPUAnimationCfg(m_Target);
            });
            EditorUtility.SetDirty(m_Target);
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
        }
        
        
        public static void CreateGPUAnimationCfg(GPUAnimatorInstancerForBake target)
        {
            var path = GPUAnimationBaker.GetAssetFolder(target.gameObject);

            var fileName = $"{target.gameObject.name}_cfg.asset";
            var newPath = $"{path}/{fileName}";
            bool newCreate = false;
            var genFile = AssetDatabase.LoadAssetAtPath<GPUAnimatorGenConfig>(newPath);
            if (genFile == null)
            {
                newCreate = true;
                genFile = ScriptableObject.CreateInstance<GPUAnimatorGenConfig>();
            }
            
            #region 拷贝数据
            
            genFile.animationData = target.animationData;
            genFile.animatorShadowLod = target.animatorShadowLod;
            genFile.boxExtents = target.boxExtents;
            genFile.frameRate = target.frameRate;
            genFile.prefabName = target.prefabName;
            if (target.needBakeAnchorInfo)
            {
                genFile.anchorInfo = AssetDatabase.LoadAssetAtPath<TextAsset>(target.anchorInfo);
            }
            else
            {
                genFile.anchorInfo = null;
            }
            

            if (genFile.LodDatas == null)
            {
                genFile.LodDatas = new List<SkinMeshLodData>();
            }
            else
            {
                genFile.LodDatas.Clear();
            }

            foreach (var bakeLodData in target.LodDatas)
            {
                var skinMeshLodData = new SkinMeshLodData();
                skinMeshLodData.lodDis = bakeLodData.lodDis;
                skinMeshLodData.renderDatas = new List<SkinMeshRenderData>();

                foreach (var bakeSkinData in bakeLodData.renderDatas)
                {
                    var skinMeshRenderData = new SkinMeshRenderData();
                    skinMeshRenderData.shadowCastingMode = bakeSkinData.meshRenderer.shadowCastingMode;
                    skinMeshRenderData.transformOffset = bakeSkinData.transformOffset;
                    skinMeshRenderData.sharedMaterial = bakeSkinData.meshRenderer.sharedMaterial;

                    if (bakeSkinData.meshRenderer is SkinnedMeshRenderer)
                    {
                        skinMeshRenderData.isSkinMesh = true;
                        var skinMeshRenderer = bakeSkinData.meshRenderer as SkinnedMeshRenderer;
                        if (skinMeshRenderer != null)
                        {
                            skinMeshRenderData.name = skinMeshRenderer.name;
                            var smd = target.animationData.GetSkinnedMeshDataByName(skinMeshRenderData.name);
                            if (smd != null)
                            {
                                var tmpMesh = InstancerUtil.GenerateMeshWithUVs(skinMeshRenderer.sharedMesh, smd);
                                var newMeshPath = path + $"{skinMeshRenderer.sharedMesh.name}_gpu.mesh";
                                var existingAsset = AssetDatabase.LoadAssetAtPath<Mesh>(newMeshPath);
                                if (existingAsset)
                                {
                                    EditorUtility.CopySerialized(tmpMesh, existingAsset);
                                    EditorUtility.SetDirty(existingAsset);
                                }
                                else
                                {
                                    AssetDatabase.CreateAsset(tmpMesh, newMeshPath);
                                    existingAsset = AssetDatabase.LoadAssetAtPath<Mesh>(newMeshPath);
                                }
                                skinMeshRenderData.sharedMesh = existingAsset;
                            }
                        }
                    }
                    else
                    {
                        skinMeshRenderData.isSkinMesh = false;
                        var meshFilter = bakeSkinData.meshRenderer.GetComponent<MeshFilter>();
                        if (meshFilter != null)
                        {
                            skinMeshRenderData.name = bakeSkinData.meshRenderer.name;
                            
                            //TODO @ljx 普通的mesh render暂时不重新生成mesh，如果后续不需要传fbx可以生成
                            // var newMeshPath = meshPath + $"{meshFilter.sharedMesh.name}.mesh";
                            // var existingAsset = AssetDatabase.LoadAssetAtPath<Mesh>(newMeshPath);
                            // if (existingAsset)
                            // {
                            //     EditorUtility.CopySerialized(meshFilter.sharedMesh,  existingAsset);
                            // }
                            // else
                            // {
                            //     var sharedMesh = (Mesh) Instantiate( meshFilter.sharedMesh );
                            //     AssetDatabase.CreateAsset(sharedMesh, newMeshPath);
                            //     existingAsset = AssetDatabase.LoadAssetAtPath<Mesh>(newMeshPath);
                            // }
                            
                            skinMeshRenderData.sharedMesh = meshFilter.sharedMesh;
                        }
                    }
                    
                    skinMeshLodData.renderDatas.Add(skinMeshRenderData);
                }
                genFile.LodDatas.Add(skinMeshLodData);
            }
            
            #endregion


            if(genFile)
            {
                if (newCreate)
                {
                    AssetDatabase.CreateAsset(genFile, newPath);
                }
                EditorUtility.SetDirty(genFile);
            }
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
        }
        

        [MenuItem("Assets/Instancer/烘焙gpu动画", false)]
        public static void RefreshInstancePrefab()
        {
            var selectiongObjs = Selection.GetFiltered<UnityEngine.Object>(SelectionMode.DeepAssets);
            foreach (var obj in selectiongObjs)
            {
                if (obj is GameObject)
                {
                    var selectObj = obj as GameObject;
                    if (selectObj)
                    {
                        var instancer = selectObj.GetComponent<GPUAnimatorInstancerForBake>();
                        if (instancer)
                        {
                            var editor = Editor.CreateEditor(instancer) as GPUAnimatorInstancerForBakeEditor;
                            if (editor)
                            {
                                editor.EditorCallSkinnedMeshBakeAnimations();
                            }
                        }
                    }
                }
            }
            
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
        }

        private static bool ContainMeshRenderer(SkinnedMeshRenderer mr, GPUAnimatorInstancerForBake instancerPrefab)
        {
            for (int i = 0; i < instancerPrefab.LodDatas.Count; i++)
            {
                var lodData = instancerPrefab.LodDatas[i];
                foreach (var renderData in lodData.renderDatas)
                {
                    if (renderData.meshRenderer == mr && renderData.meshRenderer != null)
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        private static void AutoAddLodGroup(GPUAnimatorInstancerForBake instancerPrefab)
        {
            if (instancerPrefab.LodDatas.Count > 0)
            {
                return;
            }

            var defaultLodDis = new int[5] {100, 550, 650, 750, 900};
            //目前lod不会超过5层
            for (int i = 0; i < 5; i++)
            {
                string lodName = $"lod{i}";
                var allTransform = instancerPrefab.gameObject.GetComponentsInChildren<Transform>();
                foreach (var trans in allTransform)
                {
                    if (trans.gameObject.name.Contains(lodName))
                    {
                        var meshRenderers = trans.gameObject.GetComponentsInChildren<SkinnedMeshRenderer>();
                        if (meshRenderers != null)
                        {
                            foreach (var msr in meshRenderers)
                            {
                                if (ContainMeshRenderer(msr, instancerPrefab))
                                {
                                    continue;
                                }

                                msr.sharedMaterial.enableInstancing = true;
                                var transformOffset = InstancerEditorUtil.GetTransformOffset(instancerPrefab.transform, msr.transform);

                                if (instancerPrefab.LodDatas.Count > i)
                                {
                                    instancerPrefab.LodDatas[i].renderDatas.Add(new SkinMeshRenderDataForBake()
                                    {
                                        meshRenderer = msr,
                                        transformOffset = transformOffset
                                    });
                                }
                                else
                                {
                                    var lodData = new SkinMeshLodDataForBake();
                                    lodData.lodDis = defaultLodDis[i];
                                    lodData.renderDatas = new List<SkinMeshRenderDataForBake>();
                                    lodData.renderDatas.Add(new SkinMeshRenderDataForBake()
                                    {
                                        meshRenderer = msr,
                                        transformOffset = transformOffset
                                    });
                                    instancerPrefab.LodDatas.Add(lodData);
                                }
                            }
                        }
                    }
                }
            }

            instancerPrefab.LodDatas.Sort();
        }
    }
}
